/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package se.hed.creature;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

/**
 *
 * @author fredrik
 */
public class HomoSapiensGroup implements Group{
    private Map<String, Integer> wealth = new HashMap<String, Integer>();
    private Map<String, Skill> knowledge = new HashMap<String, Skill>();
    private Map<String, List<HomoSapiens>> members = new HashMap<String, List<HomoSapiens>>();
    private Map<Integer, Integer> land = new HashMap<Integer, Integer>();
    private Logger log = null;
    private LearnStrategy learnStrategy = new WoodsMenLearnStrategi();
    private Random r = new Random();
    int year = 0;

    public HomoSapiensGroup(String name, Map<String, List<HomoSapiens>> memb) {
        members = memb;
        knowledge.put("HUNTER", WoodsMenLearnStrategi.HUNTER);
        setLogger(new PrintLogger(name));
    }

    public HomoSapiensGroup(String name) {
        knowledge.put("HUNTER", WoodsMenLearnStrategi.HUNTER);
        setLogger(new PrintLogger(name));
    }

    public void addMember(HomoSapiens hs) {
        List<HomoSapiens> hsl = null;
        if (members.containsKey(hs.getSkillName())) {
            hsl = members.get(hs.getSkillName());
        }
        else {
            hsl = new ArrayList<HomoSapiens>();
            knowledge.put(hs.getSkillName(),hs.getSkill());
        }
        hsl.add(hs);
        members.put(hs.getSkillName(), hsl);
    }
 

    public void setLogger(Logger log) {
        this.log = log;
    }

    public Map<String, Integer> getWealth() {
        return wealth;
    }

    public int getWealth(String harvestType) {
        if (wealth.containsKey( harvestType)) {
            return wealth.get(harvestType);
        }
        return 0;
    }

    public Map<String, Skill> getKnowledge() {
        return knowledge;
    }

    public Map<String, List<HomoSapiens>> getMembers() {
        return members;
    }

    public int getNbrMembers() {
        int nbr = 0;
        for (String type : members.keySet()) {
            nbr =+ members.get(type).size();
        }
        return nbr;
    }

    public Map<Integer, Integer> getLand() {
        return land;
    }

    public void setLearnStrategy(LearnStrategy strategy) {
        learnStrategy = strategy;
    }


    public void live() {
        year++;
        birth();
        die();
        consume();
        produce();
        learn();
        log.log("year: " + year);
        StringBuilder sb = new StringBuilder();
        for (String type : members.keySet()) {
            sb.append(type).append(" [");
            for (HomoSapiens h : members.get(type)) {
                sb.append(year-h.getBirthYear()).append(" ");
            }
            sb.append("]");
        }
        log.log("members: " + sb);
        log.log("wealth: " +  wealth);
    }

    public String learn() {
        Set<Skill> learnings = learnStrategy.possibleLearnings(knowledge);
        // TOOD select best skill
        Skill bestLearning = learnStrategy.selectBestLearning(learnings, knowledge);
        if (bestLearning != null) {
            knowledge.put(bestLearning.getName(), bestLearning);
            log.log("learn " + bestLearning.getName());
            return bestLearning.getName();
        }
        return null;
    }

    public int birth() {
        int total = 0;
        TypeCounter result = new TypeCounter();

        for (String memberType : members.keySet()) {
            double chans = 0.3;
            int children = 0;
            for (int i = 0;i < members.get(memberType).size();i++) {
                if (r.nextDouble() < chans) {
                    children++;
                }
            }

            total = total + children;
        }

//        log.log("birth - upcomming childrern " + total);
        for (int i = 0; i < total; i++) {
            Skill profession = learnStrategy.mostNeededSkill(knowledge.values(), learnStrategy.getNeed(this));

            HomoSapiens child = new HomoSapiens();
            child.setSkill(profession);
            child.setBirthYear(year);

            List<HomoSapiens> hsl = null;
            if (members.containsKey(profession.getName())) {
                hsl = members.get(profession.getName());
            }
            else {
                hsl = new LinkedList<HomoSapiens>();
            }
            hsl.add(child);
            members.put(profession.getName(), hsl);
            result.add(profession.getName(), 1);
        }

        StringBuilder sb = new StringBuilder();
        for (String memberType : members.keySet()) {
            if (sb.length() > 0) sb.append(", ");
            sb.append(memberType).append("=").append(members.get(memberType).size());
        }
        sb.insert(0, "{");
        sb.append("}");
        log.log("birth " + result + " total " + sb);
        return total;
    }

    public int die() {
        int total = 0;
        int dead = 0;
        Map<String, Integer> result = new HashMap<String, Integer>();
        Map<String, List<HomoSapiens>> copy = new HashMap<String, List<HomoSapiens>>();
        copy.putAll(members);

        StringBuilder sb = new StringBuilder();
        for (String memberType : copy.keySet()) {
            List<Integer> ageOfDeath = die(memberType);
            dead = ageOfDeath.size();
            if (!ageOfDeath.isEmpty()) sb.append(memberType).append(" ").append(ageOfDeath.toString()).append(" ");
            total = total + dead;
            result.put(memberType, dead);
            if (members.get(memberType).isEmpty()) {
                members.remove(memberType);
            }
        }

        log.log("die " + sb);
        return total;
    }

    public List<Integer> die(String memberType) {
        List<Integer> ageOfDeath = new ArrayList<Integer>();
        if (!members.containsKey(memberType)) {
            return ageOfDeath;
        }
        // TODO: depends on civilization level + polution level...
        double MAX_AGE = 100;
        int dead = 0;
        for (int i = 0;i < members.get(memberType).size();i++) {

            if ((survivalPercent(year - members.get(memberType).get(i).getBirthYear())) > r.nextDouble()) {
                dead++;
                ageOfDeath.add(new Integer(year - members.get(memberType).get(i).getBirthYear()));
                members.get(memberType).remove(i);
            }
        }
        return ageOfDeath;
    }

   double survivalPercent(int age) {
       switch (age) {
           case 0:
           case 1:
           case 2:
//               return 0.007;
           case 3:
           case 4:
           case 5:
//               return 0.005;
           case 50:
           case 51:
           case 52:
           case 53:
           case 54:
           case 55:
           case 56:
           case 57:
           case 58:
           case 59:
//               return 0.025;
           default:
               return 0.99;

       }
   }

    public Map<String, Integer> produce() {
        TypeCounter result = new TypeCounter();
        for (String memberType : members.keySet()) {
            List<HomoSapiens> hsl = members.get(memberType);
            int harvest = 0;
            String harvestType = null;
            for (HomoSapiens homoSapiens : hsl) {
                harvest += homoSapiens.harvest(); //skill.resultQuantity();
                harvestType = homoSapiens.getHarvestType();//skill.resultType();
            }
            int existingHarvest = getWealth(harvestType);
            wealth.put(harvestType, harvest + existingHarvest);
            result.add(harvestType, harvest);
        }
        log.log("produced " + result + " total " + wealth);
        return result;
    }


    public int consume() {
        int total = 0;
        for (String memberType : members.keySet()) {
            total += members.get(memberType).size();
        }

        // starving population...
        // TODO improve here...
        if (!wealth.containsKey("FOOD")) {
            TypeCounter result = consumeStarv(total);
            log.log("consume " + 0 + " FOOD. Starving " + (total) + " die " + result);
        }
        else if (total > wealth.get("FOOD")) {
            TypeCounter result = consumeStarv( total - wealth.get("FOOD"));
            log.log("consume " + (wealth.get("FOOD") - total) + " FOOD. Starving " + (total - wealth.get("FOOD")) + " die " + result);
        }
        else {
            wealth.put("FOOD", wealth.get("FOOD") - total);
            log.log("consume " + total + " FOOD ");
        }

        return total;
    }

    private TypeCounter consumeStarv(int suffering) {
        TypeCounter result = new TypeCounter();
        Map<String, List<HomoSapiens>> copy = new HashMap<String, List<HomoSapiens>>();
        copy.putAll(members);
        while (suffering > 0) {
            for (String memberType : copy.keySet()) {
                if (suffering == 0) {
                    break;
                }
                die(memberType);
                result.add(memberType, 1);
                suffering--;
            }
        }
        wealth.put("FOOD", 0);
        return result;
    }


    public int grow() {
        return 0;
    }

    public void environmentalChange() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void interfere() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public Map<String, Integer> promote() {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}
